
#include <libcoco.h>
#include <compl/candy.h>
#include "mprompt.h"

#include <stdlib.h>

// // Continue with `fun(p,arg)` under a fresh prompt `p`.
// mp_decl_export void* mp_prompt(mp_start_fun_t* fun, void* arg); 

// // Yield back up to a parent prompt `p` and run `fun(r,arg)` from there, where `r` is a `mp_resume_t` resumption.
// mp_decl_export void* mp_yield(mp_prompt_t* p, mp_yield_fun_t* fun, void* arg);

// // Resume back to the yield point with a result; can be used at most once.
// mp_decl_export void* mp_resume(mp_resume_t* resume, void* arg);	  // resume 
// mp_decl_export void* mp_resume_tail(mp_resume_t* resume, void* arg); // resume as the last action in a `mp_yield_fun_t`
// mp_decl_export void  mp_resume_drop(mp_resume_t* resume);			// drop the resume object without resuming

#define CROUT_MALLOC(size)	  __builtin_malloc(size)
#define CROUT_FREE(ptr, size)   __builtin_free(ptr)

typedef struct crout_int{
	// word_t   stk_size;
	void		*prompt_res;
	mp_prompt_t *prompt;
	mp_prompt_t *parent;
	void		*(*routine)(void*);
	void		*arg;
	unsigned long state;
}crout_int_t;

static int check = 0;
static void* async_worker(mp_prompt_t* parent, void* arg){
	crout_int_t *cr = (crout_int_t *)arg;
	cr->parent = parent;
	// printf ("async worker %p, %p!\n", cr->routine, cr->arg);
	void *ret = cr->routine(cr->arg);
	// printf ("async worker out!\n");
	return ret;
}

int 	crout_create( crout_t **pco,const crout_attr_t *attr,void *(*routine)(void*),void *arg){
	if (UNLIKELY(!check)){
		mp_config_t config = mp_config_default();
		mp_init(&config);
		check = 1;
	(void )attr;
	}
	void *p = CROUT_MALLOC(sizeof(crout_int_t));
	if (!p) return -1;
	crout_int_t * cr = (crout_int_t *)p;
	// cr->stk_size = attr->stack_size;
	cr->prompt_res = NULL;
	cr->state = 0;
	// mp_prompt
	cr->prompt = mp_prompt_create();
	cr->routine = routine;
	cr->arg = arg;
	*pco = (crout_t *)cr;
	return 0;
}

void	crout_resume( crout_t *co){
	crout_int_t *cr = (crout_int_t *)co;
	if(!cr->state){
		cr->state = 1;
		cr->prompt_res = mp_prompt_enter(cr->prompt, async_worker, (void *)cr);
	}
	else{
		// printf("resume\n");
		cr->prompt_res = mp_resume(cr->prompt_res, 0);
	}
}

static void* await_result(mp_resume_t* r, void* arg) {
  (void)(arg);
  return r;  // instead of resuming ourselves, we return the resumption as a "suspended async computation" (A)
}

void	crout_yield( crout_t *co ){
	crout_int_t *cr = (crout_int_t *)co;
	mp_yield(cr->parent, &await_result, NULL);
}

void	crout_release( crout_t *co ){
	crout_int_t *cr = (crout_int_t *)co;
	CROUT_FREE(cr, sizeof(crout_int_t));
}

void	crout_reset(crout_t * co){
	crout_int_t *cr = (crout_int_t *)co;
	// cr->prompt->pres = NULL;
	cr->state = 0;
	// cr->prompt_res = NULL;
	cr->prompt = mp_prompt_create();
}
